﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2012-09-11
//
// Update Date : 
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//----------------------------------------------------------------
#ifndef __FUI_SIMPLESTR_H__
#define __FUI_SIMPLESTR_H__
#pragma once


template< typename N >
FINLINE N FUI_AlignUp(N n, unsigned long nAlign)
{
    return( N( (n+(nAlign-1))&~(N( nAlign )-1) ) );
}

template< typename N >
FINLINE N FUI_AlignDown(N n, unsigned long nAlign)
{
    return( N( n&~(N( nAlign )-1) ) );
}


struct IFUI_StringObject;


class IFUI_StringAllocator
{
public:
    IFUI_StringAllocator(){}
    virtual~IFUI_StringAllocator(){}
    virtual IFUI_StringObject* Allocate(int nAllocLength, int nCharSize) = 0;
    virtual void Free( IFUI_StringObject* pData ) = 0;
    virtual IFUI_StringObject* Reallocate(IFUI_StringObject* pData, int nAllocLength, int nCharSize) = 0;
    virtual IFUI_StringObject* GetNilString() = 0;
    virtual IFUI_StringAllocator* Clone() = 0;
};


extern FAPI IFUI_StringAllocator* FUI_GetStringAllocator();
extern FAPI LONG FUI_InterlockedIncrement(LONG FVOLATILE *lpAddend);
extern FAPI LONG FUI_InterlockedDecrement(LONG FVOLATILE *lpAddend);
extern FAPI size_t FUI_StrnlenW(const WCHAR* wcs, size_t maxsize);
extern FAPI size_t FUI_StrnlenA(const ACHAR* str, size_t maxsize);
extern FAPI int FUI_WStrLength(const ACHAR* src);
extern FAPI int FUI_AStrLength(const WCHAR* src);
extern FAPI void FUI_WStrToAStrNS(const WCHAR* src, ACHAR* dst);
extern FAPI void FUI_AStrToWStrNS(const ACHAR* src, WCHAR* dst);
extern FAPI int FUI_StrcasecmpW(const WCHAR *s1, const WCHAR *s2);
FINLINE int FUI_StricmpW(const WCHAR* left, const WCHAR* right);
FINLINE int FUI_StricmpA(const ACHAR* left, const ACHAR* right);
extern FAPI WCHAR* FUI_StrToUpperW(WCHAR* str);
extern FAPI ACHAR* FUI_StrToUpperA(ACHAR* str);
extern FAPI WCHAR* FUI_StrToLowerW(WCHAR* str);
extern FAPI ACHAR* FUI_StrToLowerA(ACHAR* str);
FINLINE size_t FUI_StrlenW(const WCHAR* s);
FINLINE size_t FUI_StrlenA(const ACHAR* s);
FINLINE int FUI_StrcmpW(const WCHAR* left, const WCHAR* right);
FINLINE int FUI_StrcmpA(const ACHAR* left, const ACHAR* right);


struct FAPI IFUI_StringObject
{
    IFUI_StringObject(){}
    virtual~IFUI_StringObject(){}

    IFUI_StringAllocator* pStringMgr;  // String manager for this IFUI_StringObject
    int nDataLength;  // Length of currently used data in XCHARs (not including terminating null)
    int nAllocLength;  // Length of allocated data in XCHARs (not including terminating null)
    FVOLATILE LONG nRefs; // Reference count: negative == locked

    void* data()
    {
        return (this+1);
    }
    void AddRef()
    {
        FUI_InterlockedIncrement(&nRefs);
    }
    bool IsLocked() const
    {
        return nRefs < 0;
    }
    bool IsShared() const
    {
        return( nRefs > 1 );
    }
    void Lock()
    {
        nRefs--;  // Locked buffers can't be shared, so no interlocked operation necessary
        if( nRefs == 0 )
        {
            nRefs = -1;
        }
    }
    void Release()
    {
        if( FUI_InterlockedDecrement( &nRefs ) <= 0 )
        {
            pStringMgr->Free( this );
        }
    }
    void Unlock()
    {
        if(IsLocked())
        {
            nRefs++;  // Locked buffers can't be shared, so no interlocked operation necessary
            if( nRefs == 0 )
            {
                nRefs = 1;
            }
        }
    }
};


template< typename BaseType = ACHAR >
class IFUI_StringTraitsBase
{
public:
    typedef ACHAR XCHAR;
    typedef ACHAR* PXSTR;
    typedef const ACHAR* PCXSTR;
    typedef WCHAR YCHAR;
    typedef WCHAR* PYSTR;
    typedef const WCHAR* PCYSTR;
};


template<>
class IFUI_StringTraitsBase< WCHAR >
{
public:
    typedef WCHAR XCHAR;
    typedef WCHAR* PXSTR;
    typedef const WCHAR* PCXSTR;
    typedef ACHAR YCHAR;
    typedef ACHAR* PYSTR;
    typedef const ACHAR* PCYSTR;
};


template< typename BaseType >
class FUI_SimpleStringT
{
public:
    typedef FUI_SimpleStringT _MyClass;
    typedef IFUI_StringAllocator _IStrAllocate;
    typedef IFUI_StringObject _IStrObject;

    typedef typename IFUI_StringTraitsBase< BaseType >::XCHAR XCHAR;
    typedef typename IFUI_StringTraitsBase< BaseType >::PXSTR PXSTR;
    typedef typename IFUI_StringTraitsBase< BaseType >::PCXSTR PCXSTR;
    typedef typename IFUI_StringTraitsBase< BaseType >::YCHAR YCHAR;
    typedef typename IFUI_StringTraitsBase< BaseType >::PYSTR PYSTR;
    typedef typename IFUI_StringTraitsBase< BaseType >::PCYSTR PCYSTR;

public:
    FUI_SimpleStringT( _IStrAllocate* pStringMgr)
    {
        _IStrObject* pData = pStringMgr->GetNilString();
        Attach( pData );
    }

    FUI_SimpleStringT(const FUI_SimpleStringT& strSrc)
    {
        _IStrObject* pSrcData = strSrc.GetData();
        _IStrObject* pNewData = CloneData( pSrcData );
        Attach( pNewData );
    }

    FUI_SimpleStringT(PCXSTR pszSrc, _IStrAllocate* pStringMgr)
    {
        int nLength = StringLength( pszSrc );
        _IStrObject* pData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
        if( pData == NULL )
        {
            ThrowMemoryException();
        }
        Attach( pData );
        SetLength( nLength );
        CopyChars( m_pszData, nLength, pszSrc, nLength );
    }
    FUI_SimpleStringT(const XCHAR* pchSrc, int nLength, _IStrAllocate* pStringMgr)
    {
        _IStrObject* pData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
        if( pData == NULL )
        {
            ThrowMemoryException();
        }
        Attach( pData );
        SetLength( nLength );
        CopyChars( m_pszData, nLength, pchSrc, nLength );
    }
    ~FUI_SimpleStringT()
    {
        _IStrObject* pData = GetData();
        pData->Release();
    }

    operator FUI_SimpleStringT<BaseType>&()
    {
        return *(FUI_SimpleStringT<BaseType>*)this;
    }

    _MyClass& operator=(const _MyClass& strSrc)
    {
        _IStrObject* pSrcData = strSrc.GetData();
        _IStrObject* pOldData = GetData();
        if( pSrcData != pOldData)
        {
            if( pOldData->IsLocked() || pSrcData->pStringMgr != pOldData->pStringMgr )
            {
                SetString( strSrc.GetString(), strSrc.GetLength() );
            }
            else
            {
                _IStrObject* pNewData = CloneData( pSrcData );
                pOldData->Release();
                Attach( pNewData );
            }
        }

        return( *this );
    }

    _MyClass& operator=(PCXSTR pszSrc)
    {
        SetString( pszSrc );

        return( *this );
    }

    _MyClass& operator+=(const _MyClass& strSrc)
    {
        Append( strSrc );

        return( *this );
    }

    _MyClass& operator+=(PCXSTR pszSrc)
    {
        Append( pszSrc );

        return( *this );
    }

    _MyClass& operator+=(ACHAR ch)
    {
        AppendChar( XCHAR( ch ) );

        return( *this );
    }
    _MyClass& operator+=(BYTE ch)
    {
        AppendChar( XCHAR( ch ) );

        return( *this );
    }
    _MyClass& operator+=(WCHAR ch)
    {
        AppendChar( XCHAR( ch ) );

        return( *this );
    }

    XCHAR operator[](int iChar)
    {
        return( m_pszData[iChar] );
    }

    operator PCXSTR() const
    {
        return( m_pszData );
    }

    operator PXSTR() const
    {
        return( m_pszData );
    }

    void Append(PCXSTR pszSrc)
    {
        Append( pszSrc, StringLength( pszSrc ) );
    }
    void Append(
        PCXSTR pszSrc,
        int nLength)
    {
        // See comment in SetString() about why we do this
        UINT_PTR nOffset = pszSrc-GetString();

        UINT nOldLength = GetLength();
        if (nOldLength < 0)
        {
            // protects from underflow
            nOldLength = 0;
        }

        //Make sure we don't read pass end of the terminating NULL
        nLength = StringLengthN(pszSrc, nLength);

        int nNewLength = nOldLength+nLength;
        PXSTR pszBuffer = GetBuffer( nNewLength );
        if( nOffset <= nOldLength )
        {
            pszSrc = pszBuffer+nOffset;
            // No need to call CopyCharsOverlapped, since the destination is
            // beyond the end of the original buffer
        }
        CopyChars( pszBuffer+nOldLength, nLength, pszSrc, nLength );
        ReleaseBufferSetLength( nNewLength );
    }
    void AppendChar(XCHAR ch)
    {
        UINT nOldLength = GetLength();
        int nNewLength = nOldLength+1;
        PXSTR pszBuffer = GetBuffer( nNewLength );
        pszBuffer[nOldLength] = ch;
        ReleaseBufferSetLength( nNewLength );
    }
    void Append(const _MyClass& strSrc)
    {
        Append( strSrc.GetString(), strSrc.GetLength() );
    }
    void Empty()
    {
        _IStrObject* pOldData = GetData();
        _IStrAllocate* pStringMgr = pOldData->pStringMgr;
        if( pOldData->nDataLength == 0 )
        {
            return;
        }

        if( pOldData->IsLocked() )
        {
            // Don't reallocate a locked buffer that's shrinking
            SetLength( 0 );
        }
        else
        {
            pOldData->Release();
            _IStrObject* pNewData = pStringMgr->GetNilString();
            Attach( pNewData );
        }
    }
    void FreeExtra()
    {
        _IStrObject* pOldData = GetData();
        int nLength = pOldData->nDataLength;
        _IStrAllocate* pStringMgr = pOldData->pStringMgr;
        if( pOldData->nAllocLength == nLength )
        {
            return;
        }

        if( !pOldData->IsLocked() )  // Don't reallocate a locked buffer that's shrinking
        {
            _IStrObject* pNewData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
            if( pNewData == NULL )
            {
                SetLength( nLength );
                return;
            }

            CopyChars( PXSTR( pNewData->data() ), nLength,
                PCXSTR( pOldData->data() ), nLength );

            pOldData->Release();
            Attach( pNewData );
            SetLength( nLength );
        }
    }

    int GetAllocLength() const
    {
        return( GetData()->nAllocLength );
    }
    XCHAR GetAt(int iChar) const
    {
        return( m_pszData[iChar] );
    }
    PXSTR GetBuffer()
    {
        _IStrObject* pData = GetData();
        if( pData->IsShared() )
        {
            Fork( pData->nDataLength );
        }

        return( m_pszData );
    }
    PXSTR GetBuffer(int nMinBufferLength)
    {
        return( PrepareWrite( nMinBufferLength ) );
    }
    PXSTR GetBufferSetLength(int nLength)
    {
        PXSTR pszBuffer = GetBuffer( nLength );
        SetLength( nLength );

        return( pszBuffer );
    }
    int GetLength() const
    {
        return( GetData()->nDataLength );
    }
    _IStrAllocate* GetManager() const
    {
        _IStrAllocate* pStringMgr = GetData()->pStringMgr;
        return pStringMgr ? pStringMgr->Clone() : NULL;
    }
    PCXSTR GetString() const
    {
        return( m_pszData );
    }
    bool IsEmpty() const
    {
        return( GetLength() == 0 );
    }
    PXSTR LockBuffer()
    {
        _IStrObject* pData = GetData();
        if( pData->IsShared() )
        {
            Fork( pData->nDataLength );
            pData = GetData();  // Do it again, because the fork might have changed it
        }
        pData->Lock();

        return( m_pszData );
    }
    void UnlockBuffer()
    {
        _IStrObject* pData = GetData();
        pData->Unlock();
    }
    void Preallocate(int nLength)
    {
        PrepareWrite( nLength );
    }
    void ReleaseBuffer(int nNewLength = -1)
    {
        if( nNewLength == -1 )
        {
            int nAlloc = GetData()->nAllocLength;
            nNewLength = StringLengthN( m_pszData, nAlloc);
        }
        SetLength( nNewLength );
    }
    void ReleaseBufferSetLength(int nNewLength)
    {
        SetLength( nNewLength );
    }
    void Truncate(int nNewLength)
    {
        GetBuffer( nNewLength );
        ReleaseBufferSetLength( nNewLength );
    }
    void SetAt(
        int iChar,
        XCHAR ch)
    {
        int nLength = GetLength();
        PXSTR pszBuffer = GetBuffer();
        pszBuffer[iChar] = ch;
        ReleaseBufferSetLength( nLength );

    }
    void SetManager( _IStrAllocate* pStringMgr)
    {
        _IStrObject* pData = GetData();
        pData->Release();
        pData = pStringMgr->GetNilString();
        Attach( pData );
    }
    void SetString(PCXSTR pszSrc)
    {
        SetString( pszSrc, StringLength( pszSrc ) );
    }
    void SetString(
        PCXSTR pszSrc,
        int nLength)
    {
        if( nLength == 0 )
        {
            Empty();
        }
        else
        {
            UINT nOldLength = GetLength();
            UINT nOffset = pszSrc-GetString();
            // If 0 <= nOffset <= nOldLength, then pszSrc points into our
            // buffer

            PXSTR pszBuffer = GetBuffer( nLength );
            if( nOffset <= nOldLength )
            {
                CopyCharsOverlapped( pszBuffer, GetAllocLength(),
                    pszBuffer+nOffset, nLength );
            }
            else
            {
                CopyChars( pszBuffer, GetAllocLength(), pszSrc, nLength );
            }
            ReleaseBufferSetLength( nLength );
        }
    }

    PCXSTR c_str() const
    {
        return GetString();
    }
    size_t size() const
    {
        return (size_t)GetLength();
    }
    bool empty() const
    {
        return IsEmpty();
    }
    void clear()
    {
        Empty();
    }
    size_t capacity() const
    {
        return (size_t)GetAllocLength();
    }
    void reserve(size_t size)
    {
        Preallocate(size);
    }
    void resize(size_t size)
    {
        Reallocate((int)size);
        SetLength((size_t)size);
    }

public:
    friend _MyClass operator+(const _MyClass& str1, const _MyClass& str2)
    {
        _MyClass s( str1.GetManager() );

        Concatenate( s, str1, str1.GetLength(), str2, str2.GetLength() );

        return( s );
    }

    friend _MyClass operator+(const _MyClass& str1, PCXSTR psz2)
    {
        _MyClass s( str1.GetManager() );

        Concatenate( s, str1, str1.GetLength(), psz2, StringLength( psz2 ) );

        return( s );
    }

    friend _MyClass operator+(PCXSTR psz1, const _MyClass& str2)
    {
        _MyClass s( str2.GetManager() );

        Concatenate( s, psz1, StringLength( psz1 ), str2, str2.GetLength() );

        return( s );
    }

    static void FCDECL CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars)
    {
        memcpy( pchDest, pchSrc, nChars*sizeof( XCHAR ) );
    }
    static void FCDECL CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
    {
#if _MSC_VER > 1200
        memcpy_s( pchDest, nDestLen*sizeof( XCHAR ),
            pchSrc, nChars*sizeof( XCHAR ) );
#else
        memcpy( pchDest, 
            pchSrc, nChars*sizeof( XCHAR ) );
#endif
    }

    static void FCDECL CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars)
    {
        memmove( pchDest, pchSrc, nChars*sizeof( XCHAR ) );
    }
    static void FCDECL CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
    {
#if _MSC_VER > 1200
        memmove_s( pchDest, nDestLen*sizeof( XCHAR ),
            pchSrc, nChars*sizeof( XCHAR ) );
#else
        memmove( pchDest,
            pchSrc, nChars*sizeof( XCHAR ) );
#endif
    }
    static int FCDECL StringLength(const ACHAR* psz)
    {
        if( psz == NULL )
        {
            return( 0 );
        }
        return( int( FUI_StrlenA( psz ) ) );
    }
    static int FCDECL StringLength(const WCHAR* psz)
    {
        if( psz == NULL )
        {
            return( 0 );
        }
        return( int( FUI_StrlenW( psz ) ) );
    }
    static int FCDECL StringLengthN(const ACHAR* psz, size_t sizeInXChar)
    {
        if( psz == NULL )
        {
            return( 0 );
        }
        return( int( FUI_StrnlenA( psz, sizeInXChar ) ) );
    }
    static int FCDECL StringLengthN(const WCHAR* psz, size_t sizeInXChar)
    {
        if( psz == NULL )
        {
            return( 0 );
        }
        return( int( FUI_StrnlenW( psz, sizeInXChar ) ) );
    }

protected:
    static void FCDECL Concatenate(
        _MyClass& strResult,
        PCXSTR psz1,
        int nLength1,
        PCXSTR psz2,
        int nLength2)
    {
        int nNewLength = nLength1+nLength2;
        PXSTR pszBuffer = strResult.GetBuffer( nNewLength );
        CopyChars( pszBuffer, nLength1, psz1, nLength1 );
        CopyChars( pszBuffer+nLength1, nLength2, psz2, nLength2 );
        strResult.ReleaseBufferSetLength( nNewLength );
    }
    static void FCDECL ThrowMemoryException()
    {
    }

    // Implementation
private:
    void Attach( _IStrObject* pData)
    {
        m_pszData = static_cast< PXSTR >( pData->data() );
    }
    void Fork(int nLength)
    {
        _IStrObject* pOldData = GetData();
        int nOldLength = pOldData->nDataLength;
        _IStrObject* pNewData = pOldData->pStringMgr->Clone()->Allocate( nLength, sizeof( XCHAR ) );
        if( pNewData == NULL )
        {
            ThrowMemoryException();
        }
        int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1;
        CopyChars( PXSTR( pNewData->data() ), nCharsToCopy,
            PCXSTR( pOldData->data() ), nCharsToCopy );
        pNewData->nDataLength = nOldLength;
        pOldData->Release();
        Attach( pNewData );
    }
    _IStrObject* GetData() const
    {
        return( reinterpret_cast< _IStrObject* >( m_pszData )-1 );
    }
    PXSTR PrepareWrite(int nLength)
    {
        _IStrObject* pOldData = GetData();
        int nShared = 1-pOldData->nRefs;  // nShared < 0 means true, >= 0 means false
        int nTooShort = pOldData->nAllocLength-nLength;  // nTooShort < 0 means true, >= 0 means false
        if( (nShared|nTooShort) < 0 )  // If either sign bit is set (i.e. either is less than zero), we need to copy data
        {
            PrepareWrite2( nLength );
        }

        return( m_pszData );
    }
    void PrepareWrite2(int nLength)
    {
        _IStrObject* pOldData = GetData();
        if( pOldData->nDataLength > nLength )
        {
            nLength = pOldData->nDataLength;
        }
        if( pOldData->IsShared() )
        {
            Fork( nLength );
        }
        else if( pOldData->nAllocLength < nLength )
        {
            // Grow exponentially, until we hit 1G, then by 1M thereafter.
            int nNewLength = pOldData->nAllocLength;
            if( nNewLength > 1024 * 1024 * 1024 )
            {
                nNewLength += 1024 * 1024;
            }
            else
            {
                // Exponential growth factor is 1.5.
                nNewLength = nNewLength + nNewLength / 2;
            }
            if( nNewLength < nLength )
            {
                nNewLength = nLength;
            }
            Reallocate( nNewLength );
        }
    }
    void Reallocate(int nLength)
    {
        _IStrObject* pOldData = GetData();
        _IStrAllocate* pStringMgr = pOldData->pStringMgr;
        if ( pOldData->nAllocLength >= nLength || nLength <= 0)
        {
            ThrowMemoryException();
            return;
        }
        _IStrObject* pNewData = pStringMgr->Reallocate( pOldData, nLength, sizeof( XCHAR ) );
        if( pNewData == NULL )
        {
            ThrowMemoryException();
        }
        Attach( pNewData );
    }

    void SetLength(int nLength)
    {
        GetData()->nDataLength = nLength;
        m_pszData[nLength] = 0;
    }

    static _IStrObject* FCDECL CloneData( _IStrObject* pData)
    {
        _IStrObject* pNewData = NULL;

        _IStrAllocate* pNewStringMgr = pData->pStringMgr->Clone();
        if( !pData->IsLocked() && (pNewStringMgr == pData->pStringMgr) )
        {
            pNewData = pData;
            pNewData->AddRef();
        }
        else
        {
            pNewData = pNewStringMgr->Allocate( pData->nDataLength, sizeof( XCHAR ) );
            if( pNewData == NULL )
            {
                ThrowMemoryException();
            }
            pNewData->nDataLength = pData->nDataLength;
            CopyChars( PXSTR( pNewData->data() ), pData->nDataLength+1,
                PCXSTR( pData->data() ), pData->nDataLength+1 );
        }

        return( pNewData );
    }

private:
    PXSTR m_pszData;
};


typedef FUI_SimpleStringT< WCHAR > FUI_SimpleStringW;
typedef FUI_SimpleStringT< ACHAR > FUI_SimpleStringA;

#ifdef UNICODE
typedef FUI_SimpleStringW       FUI_SimpleString;
#else
typedef FUI_SimpleStringA       FUI_SimpleString;
#endif

#ifdef WIN32
typedef FUI_SimpleStringW       FFUI_SimpleString;
#else
typedef FUI_SimpleStringA       FFUI_SimpleString;
#endif

#endif
